/**
 * The contents of this file are subject to the license and copyright detailed in the LICENSE and NOTICE files at the root of the source tree and available online at
 * http://www.askosi.org/license.html
 */
package be.destin.skos.core;

import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;

import be.destin.skos.html.SkosHtmlManager;
import be.destin.util.Util;
public enum Predicate implements URI {
	//"sortKey"
	//"collection", "usage","narrowerUsage","status"
	Concept_url,Concept_icon,
	Concept_prefLabel,Concept_altLabel,Concept_hiddenLabel,
	Concept_changeNote,Concept_definition,Concept_editorialNote,Concept_example,Concept_historyNote,Concept_scopeNote,Concept_internalNote,
	Concept_exactMatch,Concept_closeMatch,
	Concept_broader,Concept_narrower,Concept_related,
	Concept_broadMatch,Concept_narrowMatch,Concept_relatedMatch,
	//TRANSITIVE cannot support Put (and Get followed by add or remove)!
	Concept_exactMatchTransitive,Concept_closeMatchTransitive,
	Concept_broaderTransitive,Concept_narrowerTransitive,Concept_relatedTransitive,
	Concept_broadMatchTransitive,Concept_narrowMatchTransitive,Concept_relatedMatchTransitive,
	Concept_allRelatedTransitive, Concept_topConcepts,
	Concept_alias,Concept_notation,
	ConceptScheme_title,ConceptScheme_description,ConceptScheme_creator,ConceptScheme_contributor,ConceptScheme_create,
	//"rights","namespace","uri","supraLanguage","internalNote"
	ConceptScheme_help,ConceptScheme_icon,ConceptScheme_display;

	/** log4j category */
	private static Logger log = Logger.getLogger(Predicate.class);

	private static int nbPredicates = Predicate.values().length;
	public static Method[] getAll = new Method[nbPredicates];
	public static Method[] putAll = new Method[nbPredicates];
	public static boolean[] readOnly = new boolean[nbPredicates];
	/*
	 * URI implemented by LinkedConcept, LocalLinkedConcept, Notation
	 * Literal implemented by Term, Url
	 */
	private static final Class[] oneObjectClass =
	{ Url.class,Url.class,
	  Term.class,Term.class,Term.class,
	  Term.class,Term.class,Term.class,Term.class,Term.class,Term.class,Term.class,
	  LinkedConcept.class,LinkedConcept.class,
	  LocalLinkedConcept.class,LocalLinkedConcept.class,LocalLinkedConcept.class,
	  LinkedConcept.class,LinkedConcept.class,LinkedConcept.class,
	  //TRANSITIVE cannot support Put (and Get followed by add or remove)!
	  ConceptIdentifier.class,ConceptIdentifier.class,
	  ConceptIdentifier.class,ConceptIdentifier.class,ConceptIdentifier.class,
	  ConceptIdentifier.class,ConceptIdentifier.class,ConceptIdentifier.class,
	  ConceptIdentifier.class,ConceptIdentifier.class,
	  LocalLinkedConcept.class,Notation.class,
	  Term.class,Term.class,Term.class,Term.class,UrlList.class,
	  Url.class,Url.class,Url.class };

	public static Class[] subjectsClass = new Class[nbPredicates];
	
//	public static Class[] objectsClass =
//	{ 
//	  UrlList.class,UrlList.class,
//	  TermList.class,TermList.class,TermList.class,
//	  TermList.class,TermList.class,TermList.class,TermList.class,TermList.class,TermList.class,TermList.class,
//	  LinkedList/*LinkedConcept*/.class,LinkedList/*LinkedConcept*/.class,
//	  LinkedList/*LocalLinkedConcept*/.class,LinkedList/*LocalLinkedConcept*/.class,LinkedList/*LocalLinkedConcept*/.class,
//	  LinkedList/*LinkedConcept*/.class,LinkedList/*LinkedConcept*/.class,LinkedList/*LinkedConcept*/.class,
//	  LinkedList/*LocalLinkedConcept*/.class,LinkedList/*Notation*/.class,
//	  TermList.class,TermList.class,TermList.class,TermList.class,UrlList.class};


	static {
		if (nbPredicates != oneObjectClass.length) {
			log.error(nbPredicates+" possible SKOS methods, "+oneObjectClass.length+" corresponding one object classes?");
		}
		for (Predicate onePredicate : Predicate.values()) {
			int i = onePredicate.ordinal();
			String upName = Util.firstUp(onePredicate.getPredicateName());
			String subjectClassName = onePredicate.getSubjectClassName();
			if ("Concept".equals(subjectClassName)) {
				subjectsClass[i] = Concept.class;
			} else /*Scheme*/ {
				subjectsClass[i] = NoScheme.class;
			}
			try {
				readOnly[i] = oneObjectClass[i].equals(ConceptIdentifier.class); // DO NOT MODIFY Transitive relations!
				getAll[i] = subjectsClass[i].getMethod("get"+upName);
			} catch (NoSuchMethodException NSM) {
				log.error(subjectClassName+".get"+upName+"(): unknown method");
			}
			try {
				if ( ! readOnly[i] ) {
					putAll[i] = subjectsClass[i].getMethod("put"+upName,oneObjectClass[i]);
				}
			} catch (NoSuchMethodException NSM) {
				log.error(subjectClassName+".put"+upName+"(): unknown method");
			}
		}
	}

	public String getSubjectClassName() {
		String current = this.toString();
		int i = current.indexOf(SchemeInterface.separator);
		return current.substring(0,i);		
	}
	
	public String getPredicateName() {
		String current = this.toString();
		int i = current.indexOf(SchemeInterface.separator);
		return current.substring(i+1);
	}

	public Class getObjectClass() {
		return Predicate.oneObjectClass[this.ordinal()];
	}

	public String stringValue() {
		return getNamespace()+getLocalName();
	}

	public String getNamespace() {
		return SchemeInterface.skosNamespace;
	}
	
	public String getLocalName() {
		return toString();
	}
	
	public static Predicate find(Resource subject, String property) {
		String prefix = null;
		if (subject instanceof NoScheme) prefix = "ConceptScheme";
		else if (subject instanceof Concept) prefix = "Concept";
		if (prefix == null) return null;
		return Predicate.valueOf(prefix+SchemeInterface.separator+property);
	}

	public static Predicate findInConcept(String property) {
		property = "concept"+SchemeInterface.separator+property.toLowerCase();
		Predicate[] types = Predicate.values();
		for (int i=0; i<types.length; i++) {
			Predicate aRel = types[i];
			if (aRel.toString().toLowerCase().equals(property)) {
				return aRel;
			}
		}
		return null;
	}

	public static String list() {
		StringBuffer result = new StringBuffer();
		Predicate[] types = Predicate.values();
		for (int i=0; i<types.length; i++) {
			Predicate aType = types[i];
			result.append(aType.toString());
			if ( (i+1) < types.length) result.append(", ");
		}
		return result.toString();
	}

/*
	public boolean equivalentResource(Resource a, Resource b) {
		if (a==null) return false;
		if (b==null) return false;
		if (a==b) return true;
		boolean youri = a instanceof URI;
		if (youri) {
			if ( ! (b instanceof URI)) {
				 log.error("Comparing different type of resources? "+a.toString()+" and "+b.toString());
				 return false;
			} else return ((URI)a).toString().equals(((URI)b).toString());
		} else {
			boolean litered = a instanceof Literal;
			if (litered) {
				if ( ! (b instanceof Literal)) {
					 log.error("Comparing different type of resources? "+a.toString()+" and "+b.toString());
					 return false;
				} else return ((Literal)a).toString().equals(((Literal)b).toString());
			}
			return false;
		}
	}
*/
	public Concept getConcept(SkosManager skosManager) {
		ConceptScheme schemeRoles = skosManager.getRoles();
		if (schemeRoles == null) return null;
		String aboutImg = this.name();
		int p = aboutImg.indexOf(SchemeInterface.separator);
		if (p < 0) return null;
		return schemeRoles.getConcept(aboutImg.substring(p+1));
	}
	
	public String toHtmlImage(SkosHtmlManager skosHtmlManager) {
		Concept currRole = getConcept(skosHtmlManager.getSkosManager());
		if (currRole == null) return null;
		return currRole.getIconAdapted(skosHtmlManager);
	}

	public String toHtmlIcon(SkosHtmlManager skosHtmlManager) {
		Concept currRole = getConcept(skosHtmlManager.getSkosManager());
		if (currRole == null) return this.name();
		String userLanguage = skosHtmlManager.getLanguage();
		String aName = currRole.getPrefLabel(userLanguage, false);
		if (aName == null || aName.length() <= 0) {
			aName = this.name();
		} else {
			aName = Util.escapeHTMLEntities(aName);
		}
		return skosHtmlManager.toHtmlImage(currRole.getIconAdapted(skosHtmlManager),aName,false);
	}
}
